package edu.caltech.android.gaussianMixture;

import Jama.Matrix;

/**
 * Convert from Matlab strings to matrix. This stuff should crash in all sorts
 * of ways if the inputs aren't valid.
 * 
 * TODO: stringToMatrix should correctly return a column or row vector if it is
 * passed a string for a vector.
 * 
 * @author mfaulk
 * 
 */
public class StringToMatrix {
	
	private StringToMatrix(){}
	
	/**
	 * Converts a Matlab matrix string to a JAMA matrix. TODO: might output only
	 * row vectors if a column or row vector string is given.
	 * 
	 * @param m
	 *            String from Matlab's mat2str command
	 * @return
	 */
	public static Matrix stringToMatrix(String m) {
		double[][] mArray = StringToMatrix.stringTo2DMatrix(m);
		return new Matrix(mArray);
	}

	/**
	 * 
	 * @param m
	 *            - String produced by Matlab's mat2str on a row or column
	 *            vector
	 * @return - Array representation of m
	 */
	public static double[] stringToVector(String m) {
		// remove outer [ and ]
		// split on ; to get rows
		// split on " " to get entries per row.
		m = trimBrackets(m);

		boolean hasSemicolon = m.contains(";");
		boolean hasSpace = m.contains(" ");

		if (hasSemicolon && hasSpace) {
			// if it contains both, there's an error
			System.err
					.println("Vector string may not contain space and semicolon");
			return new double[0];
		} else if (hasSemicolon) {
			// if this contains a ";", m must be a column vector
			String[] sArray = getRows(m);
			return stringArrayToDoubleArray(sArray);

		} else if (hasSpace) {
			// if it contains a " ", m must be a row vector
			String[] sArray = getColumns(m);
			return stringArrayToDoubleArray(sArray);

		} else {
			// if it contains neither, its a singleton
			double[] returnArray = new double[1];
			returnArray[0] = Double.parseDouble(m);
			return returnArray;
		}
	}

	/**
	 * Convert a Matlab 2D matrix string to array
	 * 
	 * @param m
	 * @return
	 */
	public static double[][] stringTo2DMatrix(String m) {
		m = trimBrackets(m);
		// this could have both ";" and " "
		// if not, its a vector
		boolean containsSemicolon = m.contains(";");
		boolean containsSpace = m.contains(" ");

		if (containsSemicolon && containsSpace) {
			// a full 2D matrix to parse. Get the rows, then parse the rows
			String[] rows = getRows(m);
			int nRows = rows.length;
			double[][] returnArray = new double[nRows][];
			for (int i = 0; i < nRows; i++) {
				String[] columnEntries = getColumns(rows[i]);
				returnArray[i] = stringArrayToDoubleArray(columnEntries);
			}
			return returnArray;
		} else if (containsSemicolon || containsSpace) {
			// TODO: this only returns a row vector. Should be two cases
			double[][] returnArray = new double[1][];
			returnArray[0] = stringToVector(m);
			return returnArray;
		} else {
			// singleton
			double[][] returnArray = new double[1][1];
			returnArray[0][0] = Double.parseDouble(m);
			return returnArray;
		}
	}

	/**
	 * Remove leading and trailing brackets '[' and ']' if present
	 * 
	 * @param s
	 * @return
	 */
	private static String trimBrackets(String s) {
		if (s.startsWith("[")) {
			String regex = "[\\[,\\]]"; // that's [ and ] inside of a [], with
										// // to escape
			String[] result = s.split(regex);
			return result[1];
		} else {
			// doesn't seem to be begin and end with brackets
			return s;
		}
	}

	/**
	 * 
	 * @param s
	 * @return
	 */
	private static String[] getRows(String s) {
		return s.split(";");
	}

	/**
	 * 
	 * @param s
	 * @return
	 */
	private static String[] getColumns(String s) {
		return s.split(" ");
	}

	/**
	 * 
	 * @param sArray
	 *            Must contain space separated strings that can be parsed to
	 *            Doubles. Scientific notation is okay.
	 * @return
	 */
	private static double[] stringArrayToDoubleArray(String[] sArray) {
		// convert to doubles
		int nEntries = sArray.length;
		double[] returnArray = new double[nEntries];

		for (int i = 0; i < nEntries; i++) {
			String e = sArray[i];
			returnArray[i] = Double.parseDouble(e);
		}
		return returnArray;
	}

}